#nullable enable

using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PHMEE.CommonUtils;
using PHMEE.EnergyEfficiency;
using Volo.Abp.Domain.Repositories;

namespace PHMEE.Admin.Services.DataSync;

public class DataSyncShipService : BackgroundService
{
    private readonly ILogger<DataSyncShipService> _logger;
    private readonly IConfiguration _configuration;
    private readonly IPHMEETimeProvider _phmeeTimeProvider;
    private readonly ITimeSeriesDataLogRepository _timeSeriesDataLogRepository;
    private readonly ILogAbstractRepository _logAbstractRepository;
    private readonly IBunkerReportRepository _bunkerReportRepository;
    private readonly IHttpClientFactory _httpClientFactory;

    private readonly string _serverAddress;
    private readonly Guid _vesselId;
    private Task? _executingTask;
    private readonly SemaphoreSlim _syncLock = new(1, 1);

    public DataSyncShipService(ILogger<DataSyncShipService> logger,
        IConfiguration configuration,
        IPHMEETimeProvider phmeeTimeProvider,
        ITimeSeriesDataLogRepository timeSeriesDataLogRepository,
        ILogAbstractRepository logAbstractRepository,
        IBunkerReportRepository bunkerReportRepository,
        IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _configuration = configuration;
        _phmeeTimeProvider = phmeeTimeProvider;
        _timeSeriesDataLogRepository = timeSeriesDataLogRepository;
        _logAbstractRepository = logAbstractRepository;
        _bunkerReportRepository = bunkerReportRepository;
        _httpClientFactory = httpClientFactory;

        // 从配置文件中读取是否是船端

        _serverAddress = _configuration.GetValue<string>("DataSyncShip:ServerAddress") ??
                         throw new ArgumentNullException("DataSyncShip:ServerAddress", "ServerAddress 配置不能为空");
        _vesselId = _configuration.GetValue<Guid>("DataSyncShip:VesselId");
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                var utcNow = _phmeeTimeProvider.GetUtcNow();

                _logger.LogInformation("数据同步任务运行: {time}", utcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));

                try
                {
                    await _syncLock.WaitAsync(stoppingToken);
                    _executingTask = DoWorkAsync(stoppingToken);
                    await _executingTask;
                }
                catch (OperationCanceledException)
                {
                    _logger.LogInformation("数据同步任务被取消");
                    break;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "数据同步任务出现异常");
                }
                finally
                {
                    _syncLock.Release();
                }

                // 等待 1 分钟后再次执行
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
            }
        }
        catch (OperationCanceledException)
        {
            _logger.LogInformation("数据同步任务被取消");
        }
    }

    private async Task DoWorkAsync(CancellationToken stoppingToken)
    {
        // 按操作时间从早到晚排序，严格按顺序同步
        var allUnSyncedLogs =
            await _timeSeriesDataLogRepository.GetListUnSyncedOperationTimeAscAsync(stoppingToken);

        if (allUnSyncedLogs.Count == 0)
        {
            _logger.LogInformation("没有待同步数据");
            return;
        }

        var allLogIds = allUnSyncedLogs.Select(x => x.Id).ToList();
        _logger.LogInformation("CBM 开始同步数据，待同步共 {count} 条数据, id: {allLogIds}", allUnSyncedLogs.Count, allLogIds);

        foreach (var log in allUnSyncedLogs)
        {
            // 一次只同步一条数据

            // 为 log 补充数据
            if (log.OperationType == OperationType.Insert)
            {
                if (log.SyncDataType == SyncDataType.LogAbstract)
                {
                    var la = await _logAbstractRepository.GetOrDefaultAsync(_vesselId, log.Ts);
                    if (la is not null)
                    {
                        log.Datum = JsonSerializer.Serialize(la, DataSyncJsonSerializerExtensions.Options);
                    }
                }
                else if (log.SyncDataType == SyncDataType.BunkerReport)
                {
                    var br = await _bunkerReportRepository.GetAsync(_vesselId, log.Ts);
                    if (br is not null)
                    {
                        log.Datum = JsonSerializer.Serialize(br, DataSyncJsonSerializerExtensions.Options);
                    }
                }
                // 暂时不同步其他数据类型
            }
            else if (log.OperationType == OperationType.Delete)
            {
                // 删除操作不需要查出数据来
            }
            // 暂时只有 Insert 和 Delete 操作，没有其它操作

            // 调用岸端 web api 来将数据推送过去
            var httpClient = _httpClientFactory.CreateClient();
            var responseMessage = await httpClient.PostAsJsonAsync(
                $"http://{_serverAddress}/phmeeadmin/api/data-sync/SyncData",
                log, cancellationToken: stoppingToken);

            if (responseMessage.IsSuccessStatusCode)
            {
                // 如果响应是 id（int），则认为是成功
                var responseContent = await responseMessage.Content.ReadAsStringAsync(stoppingToken);
                // 返回的 responseContent 是带双引号的，需要去掉
                responseContent = responseContent.Trim('"');
                if (Guid.TryParse(responseContent, out var responseId))
                {
                    if (responseId == log.Id)
                    {
                        // 更新数据库中的数据同步状态为 true
                        log.IsSynced = true;
                        await _timeSeriesDataLogRepository.UpdateAsync(log, cancellationToken: stoppingToken);

                        _logger.LogInformation("CBM 数据同步成功，Id: {id}", log.Id);
                    }
                }
                else
                {
                    _logger.LogError("CBM 数据同步失败，Id: {id}，响应内容：{responseContent}", log.Id, responseContent);
                    
                    // 如果同步失败，则立刻结束这一轮同步，而不是继续同步，因为我们必须按顺序同步数据
                    // 然后等待下一轮同步
                    return;
                }
            }
            
            // 等待 10 秒再同步下一条，防止网络争用
            await Task.Delay(10000, stoppingToken);
        }

        _logger.LogInformation("本轮数据同步任务执行完成");
    }

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("数据同步任务开始运行");
        await base.StartAsync(cancellationToken);
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("数据同步任务正在停止...");

        // 等待当前任务完成
        if (_executingTask != null)
        {
            try
            {
                await _syncLock.WaitAsync(cancellationToken);
                if (_executingTask != null)
                {
                    await _executingTask;
                }
            }
            finally
            {
                _syncLock.Release();
            }
        }

        _logger.LogInformation("数据同步任务已停止");
        await base.StopAsync(cancellationToken);
    }
}